---
sidebar_position: 7
---

import Mermaid from "@theme/Mermaid";
import listItemPlan from "../examples/step-classes/listItem.mermaid?raw";

# Step classes

A step details a particular action or transform that needs to be performed when
executing a GraphQL request. Each step is an instance of a specific _step
class_, produced during the planning of a field. Each step may depend on 0 or
more other steps, and through these dependencies ultimately form a directed
acyclic graph which we refer to as the _execution plan_. Thus the steps are the
building blocks of an execution plan.

A range of [standard step classes][standard steps] are available for you
to use; but when these aren't enough you are encouraged to write your own (or
pull down third party step classes from npm or similar).

Step classes extend the `Step` class, the only required method to
define is `execute`, but you may also implement the various lifecycle methods,
or add methods of your own to make it easier for you to write [plan
resolvers][].

<!-- prettier-ignore -->
```ts
/** XKCD-221 step class @ref https://xkcd.com/221/ */
class GetRandomNumberStep extends Step {
  execute({ count }) {
    return new Array(count).fill(4); // chosen by fair dice roll.
                                     // guaranteed to be random.
  }
}

function getRandomNumber() {
  return new GetRandomNumberStep();
}
```

:::tip[Use prefixes on custom fields/methods.]

If you add any custom fields or methods to your step classes we recommend that
you prefix them with your initials or organization name to avoid naming
conflicts occurring.

:::

:::danger[Don't subclass steps.]

Don't subclass steps, this will make things very confusing for you. Always
inherit directly from `Step`.

:::

:::warning[Double underscore (`__`) prefix is reserved for internal usage]

Step classes whose names start with two underscores (`__`) are internals and
must never be directly created by the user. Further, you must never name your
own step classes such that they begin with two underscores.

:::

## Step function

By convention, we always define a function that constructs an instance of our
class. A step function is typically named after the corresponding step class,
but with the first letter lowercased and the `Step` suffix omitted; for example
the `AddStep` step class would have a step function named `add`:

```ts
function add($a, $b) {
  return new AddStep($a, $b);
}
```

There are many reasons for this indirection:

- simplicity, conciseness and readability of plan resolvers: no `new` keyword,
  no redundant `Step` text, no harsh PascalCase identifiers; feels lighter
- flexible API: the function can manipulate arguments before sending them on to
  a class constructor, allowing for overloads, complex generics, or tagged
  template literals
- caching: it's simple to add a cache at the step function level, much harder to
  handle that in the constructor of a step class
- branching: a function may choose to return a different step under certain
  circumstances, for example if it determines that the input is a constant
- evolution: a step function should express the developer's intent; how that
  maps to steps under the hood is less important - e.g. a `sideEffect()` step
  might start out by building a `LambdaStep` and marking it as having side
  effects, but later evolve to using its own `SideEffectStep` class, without
  consuming code needing to change

The tiny additional cost of these step functions is only incurred at plan-time,
and plans are re-used for similar future requests; the cost of an additional
function call during planning is negligible.

## Lifecycle methods

### execute

```ts
execute(details: ExecutionDetails): PromiseOrDirect<GrafastResultsList>
```

```ts
// These are simplified types
interface ExecutionDetails {
  count: number; // Size of the batch being processed
  values: [...ExecutionValue[]]; // Represents your dependencies
  stream: ExecutionDetailsStream | null; // Relates to `@stream`/`subscription`
  // ...
  indexMap<T>(callback: (batchIndex: number) => T): ReadonlyArray<T>;
  indexForEach(callback: (batchIndex: number) => any): void;
}
type ExecutionValue<TData = any> = {
  isBatch: boolean;
  unaryValue(): TData; // Throws if `isBatch` is true.
  at(batchIndex: number): TData; // Get data entry (`0 <= batchIndex < count`)
};
interface ExecutionDetailsStream {
  initialCount: number;
}
type GrafastResultsList<T = any> = ReadonlyArray<PromiseOrDirect<T>>;
```

The one method that your step class must define is `execute`. It uses a similar
approach to
[DataLoader's batch function](https://github.com/graphql/dataloader#batch-function),
but Gra*fast* steps are much more powerful thanks to the additional dependencies
and lifecycle methods.

When the step class adds a dependency (with `this.addDependency($step)` or
similar) a dependency index, `depIndex`, is returned. Dependency indexes start
at 0 and increase monotonically (a dependency can never be removed[^1]).

[^1]:
    To remove a dependency, you must instead replace the step with a copy that
    does not depend on that step, for example via the `optimize` lifecycle
    method.

At execution time the step is passed `ExecutionDetails` which contains
details and helpers for execution.

#### details.count

One key property is the size (`details.count`) of the batch; `execute` **must**
return a list of length `count`, such that each entry in this list corresponds
with the values in the batch at the same batch index. To save you from having to
loop yourself, the `details.indexMap` helper will call the given callback with
each index in the batch in turn; this is commonly used when returning the result
of the execute method.

#### details.values

`details.values` is also critical: an ordered array of execution values, one for
each dependency. You can retrieve the execution value for a given dependency via
its `depIndex` as `const depEv = details.values[depIndex]`. Once you have an
execution value, you can retrieve the value for a given index in the batch via
`const value = depEv.at(batchIndex)`.

Gra*fast* tracks which steps will always represent exactly one value (e.g. the
GraphQL context, input values passed as field arguments, constants, etc), and
which will represent a batch (e.g. values inside of a list). The former are
stored in "unary execution values", and the latter in "batch execution values".
For convenience, these both expose the `.at(batchIndex)` method so most of the
time you do not need to know the difference and can just think of them all as
simply "execution values".

Unary dependencies are useful for request-global concerns such as pagination
arguments, authentication credentials, database clients and the like. Should you
know that your dependency is a unary step (e.g. because you added it via
`this.addUnaryDependency($step)`), you may get its singular value via `const
value = depEv.unaryValue()` (but if it wasn't unary then this will throw!).

If you have a fixed number of dependencies it common to destructure them at the
top of the execute method:

```ts
class MyStep extends Step {
  constructor($a, $b, $c) {
    this.aDepIndex = this.addUnaryDependency($a);
    this.bDepIndex = this.addDependency($b);
    this.cDepIndex = this.addDependency($c);
  }
  execute(details) {
    const { values } = details;
    // Retrieve the execution values in the same order we added dependencies
    const [aEv, bEv, cEv] = values;
    // The unary dependency value can be shared throughout execute
    const a = aEv.unaryValue();

    // We must return a list of the correct length in the correct order.
    return details.indexMap((batchIndex) => {
      // Batch depenencies may have a different value at each index.
      const b = bEv.at(batchIndex);
      const c = cEv.at(batchIndex);

      return a + b + c;
    });
  }
}
```

:::danger[Step with no dependencies]

If the step has no dependencies then `values` will be a 0-tuple (an empty
tuple), but that doesn't mean the batch is empty or has size one, `count` may
be any positive integer. It's therefore recommended that you use `indexMap` to
generate your results in the vast majority of cases:

```ts
return indexMap((i) => 42);
```

:::

:::info[Tuple of values, rather than list of tuples?]

You might wonder why the `values` input is a tuple of execution values, rather
than a list of tuples. The reason comes down to efficiency, by using a tuple of
execution values, <Grafast /> only needs to build one new array (the tuple),
and into that array it can insert the results from previously executed steps
unmodified. Were it to provide a list of tuples instead then it would need to
build N+1 new arrays, where N was the number of values being processed, which
can easily be in the thousands.

:::

#### Avoid N+1!

**Critically**, the execute method should never do async actions whilst looping
over the values, otherwise you introduce the N+1 problem. Most `execute` methods
should only perform a single async action. Typically it looks like this:

1. Extract and identify the execution value for each dependency.
2. Map over the indices to prepare the input for your async task.
3. Execute the async task via a **single `await`** (or promise), not in a loop.
4. Map over the input values again, for each index finding the value from step 3 that correlates.

Here's a hypothetical example, concentrate on the `execute()` method and see how
the steps outline above play out:

```ts
import { languageService } from "./services/language";

class TranslationStep extends Step {
  langDepIndex: number;
  textDepIndex: number;
  constructor($language: Step<string>, $text: Step<string>) {
    super();
    // Add our dependencies
    this.langDepIndex = this.addDependency($language);
    this.textDepIndex = this.addDependency($text);
  }
  execute(details) {
    // highlight-start
    // 1. Extract and identify the execution value for each dependency:
    // highlight-end
    const langEv = details.values[this.langDepIndex];
    const textEv = details.values[this.textDepIndex];

    // highlight-start
    // 2. Map over the indices to prepare the input for our translation API:
    // highlight-end
    const specs = details.indexMap((batchIndex) => {
      const language = langEv.at(batchIndex);
      const sourceText = textEv.at(batchIndex);
      return { language, sourceText };
    });

    // highlight-start
    // 3. Execute the translations via a single `await`, not in a loop:
    // highlight-end
    const translations = await languageService.batchTranslate(specs);

    // highlight-start
    // 4. Finally, return results that correlate with the inputs:
    // highlight-end
    return indexMap((batchIndex) => {
      const language = langEv.at(batchIndex);
      const sourceText = textEv.at(batchIndex);
      const match = translations.find(
        (t) => t.language === language && t.sourceText === sourceText,
      );
      return match?.translation;
    });
  }
}
```

#### Errors

If the `execute` method throws (or rejects), then _all_ entries in the batch for
this step will fail with the same error.

If you want one of your entries to throw an error, but the others shouldn't,
then set the corresponding entry in the results list to `flagError(error)`
(where `error` is the error you want to use).

:::tip[Alternatively reject the promise for that specific item]

If it's more convenient and performance isn't a huge concern, you may return a
list of values and promises, and reject specifically the promise in that list
relating to that item. You can do this even if you don't use promises for any of
the other values, and even if your `execute` method is not marked as `async`.

:::

:::danger[No per-value errors if `isSyncAndSafe`]

You **must not** add per-value errors if you have marked your step class with
`isSyncAndSafe = true`; the results of doing so are undefined.

:::

### deduplicate

_This method is optional._

```ts
// Where `MyStep` is the current step class
deduplicate(peers: readonly MyStep[]): readonly MyStep[]
```

After a field has been fully planned, <Grafast /> will call this method on each
new step when more than one step exists in the draft execution plan with the
same step class and the same dependencies. These "peers" (including the step
itself) will be passed in to the deduplicate method, and this method should
return the list of the peers that are equivalent (or could cheaply be made
equivalent) to the current step.

To cause your step class to never be deduplicated, either don't implement this
method or simply `return [];`.

You should not mutate your peers or yourself during this method, instead use the
`deduplicatedWith` method to pass any necessary information to your replacement.

### deduplicatedWith

_This method is optional._

```ts
deduplicatedWith(replacement: Step): void
```

If <Grafast /> determines that this specific step instance should be replaced
by one of its peers (thanks to the results from `deduplicate` above), <Grafast
/> will call `deduplicatedWith` on the step that is being replaced, passing the
step that it is being replaced with as the first argument. This gives your step
a chance to pass any information to the peer that may be necessary to make the
peers truly equivalent.

:::info[When would I need this?]

It's rare to need this functionality, so let's work through a hypothetical.

Imagine step `$select1` represents the SQL query `SELECT id, name FROM users`
and step `$select2` represents `SELECT id, avatar_url FROM users`.

Lets further imagine that we've optimised our SQL handling step classes such
that both `$select1` and `$select2` return each other from their
`deduplicate` method (because they can "cheaply" be made equivalent).

Assuming <Grafast /> chooses to keep `$select1` and
"deduplicate" (get rid of) `$select2`, <Grafast /> would then call
`$select2.deduplicateWith($select1)`. This would give `$select2` a chance to
inform `$select1` that in order to be completely equivalent, it must also
select `avatar_url`.

In this scenario, at the end of deduplication, only `$select1` would remain and
it would represent the SQL query `SELECT id, name, avatar_url FROM users`.

:::

### optimize

_This method is optional._

```ts
optimize(options: { stream: {} | null, meta?: Record<string, unknown> }): Step
```

This method is called on each step during the optimize lifecycle event. It
gives the step a chance to request that its ancestors do additional work,
and/or replace itself with another step (new or old). If it does not want
to be replaced, it can simply return itself: `return this;`.

This one method unlocks a significant proportion of <Grafast />'s efficiency
improvements. Here are some common use cases that it can be used for:

#### Use case: inlining

`optimize` is often useful for "inlining" the requirements of this step into an
ancestor and then (optionally) replacing itself with a simple `access` or
`remapKeys` step. This reduces the number of asynchronous tasks the request
needs to execute and can enable significantly more efficient data fetching.

#### Use case: plan-time only steps

Another use case for `optimize` is to make planning-time only steps "evaporate"
by replacing them with their parent or a different step.

The `loadMany` step represents each record via a `LoadedRecordStep` instance
which can be used to `.get(attr)` a named attribute. This reference is then
stored, and at optimize time the `LoadedRecordStep` can tell the `LoadStep` to
request this attribute (so that the `loadMany` callback doesn't need to do the
equivalent of `SELECT *` - it can be more selective). However, since
`LoadedRecordStep` has no run-time behavior (only planning-time behavior) it
can simply replace itself during `optimize` with its parent step (typically an
`__ItemStep`).

The built-in `each` step uses `optimize` to replace itself with the underlying
list where possible.

#### Use case: simplification

Another use case is simplification.

For example the step representing `access(access(access($a, 'b'), 'c'), 'd')`
could be simplified down to just `access($a, ['b', 'c', 'd'])`, reducing the
number of steps in the operation plan.

Similarly `first(list([$a, $b]))` can be simplified to just `$a`.

#### `options.meta`

If the step sets a `this.optimizeMetaKey`, then `options.meta` will be a Record
that can be mutated during optimize. This is useful for steps of the same class
to communicate with each other out of band, for example `list()` and `object()`
use it to ensure that if they replace themselves with constants, they do so with
the same constant when representing the same values.

### finalize

_This method is optional._

```ts
finalize(): void
```

This method is called on each step during the finalize lifecycle event. It gives
each step a chance to prepare for execution, doing anything that needs to be
done just once. A step that deals with a database might precompile its SQL, a
step that transforms an object might build an optimized function to do so, there
are so many other actions that this step can be used for.

:::danger[Must call `super.finalize()` last!]

It is critical that the step calls `super.finalize()` at the end of the
`finalize()` step:

```ts
finalize() {
  // ... your code here ...

  super.finalize();
}
```

:::

:::warning[Do not communicate with other steps!]

Importantly during this lifecycle event the step should only worry about its own
concerns and should not attempt to communicate with its ancestors or descendents
&mdash; they may not be the steps that it remembers as they may have been
switched out during `optimize`! If the step needs to communicate with its
ancestors it should use the `optimize` method to do so.

:::

## Custom methods and conventions

Your step may implement any additional methods that it needs; however certain methods
have special meaning. For example, if your step represents an object then it should
implement the `.get(key)` method; and if the step represents an array/list then it
should implement the `.at(index)` method.

These conventions are still evolving, and more may be added as common usage patterns are
detected. Some likely candidates for future reserved methods include: import, export,
defer, filter, order and merge. To avoid conflicts with built in and future methods,
consider using a prefix when naming custom methods.

Functions that have special meanings/expectations can be found below:

### at

```ts
at(index: number): Step
```

Implement `.at()` if your step represents a list or an array. It should accept a single argument, an
integer, which represents the index within the list-like value which should be accessed.

Usage:

```ts
import { access } from "grafast";

class MyListStep extends Step {
  // ...

  at(index) {
    // Your step may implement a more optimized solution here.
    return access(this, index);
  }
}
```

:::warning[Must follow convention!]

If your step implements `.at()`, make sure it meets the expectations: i.e. it
correctly accepts a single argument: an integer. <Grafast /> relies on this
assumption; unanticipated behaviours may result from steps which don't adhere to
these expectations.

:::

### get

```ts
get(key: string): Step
```

Implement `.get()` if your step represents an object. It should accept a single argument, a
string, which represents an attribute to access an object-like value.

```ts
import { access } from "grafast";

class MyObjectStep extends Step {
  // ...

  get(key) {
    // Your step may implement a more optimized solution here.
    return access(this, key);
  }
}
```

:::warning[Must follow convention!]

If your step implements `.get()`, make sure it meets the expectations:
i.e. it correctly accepts a single argument of a string.
&ZeroWidthSpace;<Grafast /> relies on this assumption; unanticipated behaviours may result
from steps which don't adhere to these expectations.

:::

:::tip[Implementing `.get(key)` with per-key type safety]

If you have put effort into making your `get` function type safe (such that
accessing different keys returns different values), this will _not_ work with
[`get($step, key)`](./standard-steps/get.md) out of the box due to limitations
of TypeScript.

To work around this, you can add the `__inferGet` fake property to your class
which should be an object with the same keys as your `get` function accepts, and
the value should be the return type for that key - i.e. it's very similar to
your `get` method itself:

```ts
class MyStep extends Step {
  __inferGet?: {
    [TKey in keyof MyData]: MySpecialStep<MyData[TKey]>;
  };
  get<TKey extends keyof MyData>(key: TKey): MySpecialStep<MyData[TKey]> {
    // ...
  }
}
```

:::

### items

```ts
items(): Step<any[]>
```

Implement `.items()` if your step represents a collection and you want to give
users an easy way of accessing the items of your collection (as opposed to
metadata you may also wish to make available, such as pagination info). It
should accept no arguments (later <!-- Benjie: see commit
8f5ccf8592d80b0addc942951e96659292763c4d --> we might support options related
to streaming, so do not implement arguments!) and it should expect to be called
zero or more times.

```ts
import { access } from "grafast";

class MyCollectionStep extends Step {
  // ...

  items() {
    // Update this to access the correct property needed for the items in your
    // collection; you may also choose to track that this was requested and
    // thus ensure that fetches only go ahead when necessary.
    return access(this, "items");
  }
}
```

:::warning[Must follow convention!]

If your step implements `.items()`, make sure it meets the expectations:
i.e. it does not require any arguments.
&ZeroWidthSpace;<Grafast /> relies on this assumption; unanticipated behaviours may result
from steps which don't adhere to these expectations.

:::

### apply

```ts
apply($cb: Step<(parent: any) => any): void
```

Implement `.apply()` if your step wants to allow for runtime modification of
its action based on non-trivial input values - for example, if your step
represents an SQL query it might want to allow dynamic `WHERE` or `ORDER BY`
clauses based on input arguments to a GraphQL field. `.apply()` will accept a
single argument, a step that represents a runtime callback function. The step
should then call this function from `.execute()` before running its main
action.

For more information, see [handling complex inputs](./plan-resolvers/complex-inputs.md).

### toTypename

```ts
toTypename($step: Step): Step<string>
```

Enables a step to return a step that yields the typename, used by the
`defaultPlanType` polymorphic type resolver function when the GraphQL union or
interface type does not implement the `planType` method. If not implemented,
this default function will fall back to `get($step, '__typename')`.

### toSpecifier

```ts
toSpecifier($step: Step): Step
```

This function name is reserved for convenience such that `$step.toSpecifier()`
should mean the same as `abstractType.toSpecifier($step)`. Gra*fast* does not
actually use this method (currently), but it can be convenient for users so
we reserve it for specifically this use case.

Importantly, Gra*fast* does not require that a specifier takes a
particular form, it's an agreement between the steps you're using and the
polymorphic types (unions and interfaces) that you've implemented. We strongly
recommend it's a plain-old JavaScript object (POJO) though!

```ts
class MyStep extends Step {
  // ...
  toSpecifier() {
    return object({
      __typename: this.get("type"),
      id: this.get("id"),
    });
  }
}

const Animal = new GraphQLInterfaceType({
  name: "Animal",
  // ... fields ...
  toSpecifier($step) {
    // Call .toSpecifier() if it exists, otherwise use the step directly
    return $step.toSpecifier?.() ?? $step;
  },
  planType($specifier, info) {
    // Extract the property that indicates the type from above
    const $__typename = get($specifier, "__typename");
    return { $__typename };
  },
});

const Cat = new GraphQLObjectType({
  name: "Cat",
  interfaces: [Animal],
  // ... fields ...
  extensions: {
    grafast: {
      planType($stepOrSpecifier) {
        const $id = get($specifier, "id");
        return cats.get({ id: $id });
      },
    },
  },
});
```

### listItem

```ts
listItem($item: __ItemStep): Step
```

If your step represents a list, it may implement `.listItem()` to wrap the
Gra*fast* internal `__ItemStep` that represents items of this list in a more
useful step for child [field plan resolvers](./plan-resolvers/index.mdx) to
use.

A step class that implements `.listItem()` is called a "list-capable step" (it
implements `ListCapableStep`).

#### Example

Here our list capable step wraps the `__ItemStep` in a `MyListItemStep` to
ensure that the child field plan resolvers have access to the methods they
expect:

```ts
class MyListStep extends Step implements ListCapableStep {
  listItem($item: __ItemStep): SingleItemFromMyListStep {
    return myListItem($item);
  }
}
```

This might result in a plan diagram as such:

<Mermaid chart={listItemPlan} />

## Built in methods

Your custom step class will have access to all the built-in methods that come
as part of `Step`.

### addDependency

When your step requires another step's value in order to execute (which is the
case for the majority of steps!) it must add a dependency via the
`this.addDependency($otherStep)` method. This method will return a number,
which is the index in the `execute` values tuple that represents this step.

It's common to do this in the constructor, but it can be done at other stages
too, for example during the optimize phase a step's descendent might ask it to
do additional work, and that work might depend on another step.

In the [getting started][] guide we saw the constructor for the `AddStep` step
class added two dependencies:

```ts
class AddStep extends Step {
  constructor($a, $b) {
    super();
    this.addDependency($a); // Returns 0
    this.addDependency($b); // Returns 1
  }
}
```

:::danger Steps are ephemeral, never store a reference to a step.

You must never store an instance of another step directly (or indirectly) in
your step class. Steps come and go at quite a rate during planning - being
removed due to deduplicate, optimize, or tree shaking lifecycle events.
Referring to a step that no longer exists in the execution plan is likely to
make your program have very unexpected behaviors and/or crash.

In the exceedingly unlikely event that you need to reference another step but it
is not a dependency, use:
`this.refIdx = this.addRef($step, "[ENTER REASON WHY I'M NOT USING addDependency HERE]")`.
You can then use `const $ref = this.getRef(this.refIdx)` to retrieve the step at
a later time; if it exists it may be different to the step you remember, but it
should serve the same purpose. However, it may have been deleted due to tree
shaking - if this causes a problem, then maybe that step should have been a
dependency after all?

:::

### addUnaryDependency

Sometimes you'll want to ensure that one or more of the steps your step class
depends on will have exactly one value at runtime; to do so, you can use
`this.addUnaryDependency($step)` rather than `this.addDependency($step)`. This
asserts that the given dependency is a **unary step** (a regular step which the
system has determined will always represent exactly one value) and is primarily
useful when a parameter to a remote service request needs to be the same for
all entries in the batch; typically this will be the case for ordering,
pagination and access control.

:::warning Only for use with steps which will always be unary

`this.addUnaryDependency($step)` will raise an error during planning if the
given `$step` is not unary, so you should be very careful using it. If in
doubt, use `this.addDependency($step)` instead.

The system steps which represent request–level data (e.g. context, variable and
argument values) are always unary steps, and &ZeroWidthSpace;<Grafast /> will
automatically determine which other steps are also unary steps.

It's generally intended for `addUnaryDependency` to be used for arguments and
their derivatives; it can also be used with `context`-derived values, but there
is complexity when it comes to mutations since `context` is mutable (whereas
input values are not).

:::

### getDep

Pass in the number of the dependency (`0` for the first dependency, `1` for the
second, and so on) and Gra*fast* will return the corresponding step. This should
only be used before or during the `optimize` phase.

For example in the `AddStep` example above we might have:

```ts
const $a = this.getDep(0);
const $b = this.getDep(1);
```

### getDepDeep

_EXPERIMENTAL_

Like `getDep`, but skips over `__ItemStep` and similar built-in intermediary
steps to try and get to the original source. Typically useful if you have a
step representing an entry from a collection (e.g. a database "row") and you
want to get the step representing the entire collection (e.g. a database
`SELECT` statement).

### toString

Pretty formatting for the step.

```ts
console.log("$a = " + $a.toString());
```

### toStringMeta

You may override this to add additional data to the `toString` method (the data
that would occur between the triangular brackets).

## Other properties

### id

Every step is assigned a unique id by <Grafast />. This id may be a string, number,
or symbol - treat it as opaque.

:::note

Currently this value is a `number`, but <Grafast /> may change it to be a string or
symbol in a minor release so you should not rely on its data type. You may,
however, rely on `String(id)` being unique across an operation plan.

:::

### hasSideEffects

Set this true if the step has side effects (i.e. causes a mutation) - if this
is true then Gra*fast* will _not_ remove this step during tree shaking, and
will ensure that the step is executed even if it doesn't appear to be used in
any output.

:::info[Network requests are not side effects]

In languages like Scala/Haskell, a side effect is anything that is visible
outside of the function/component itself: a network call, logging, etc. You
might have heard that "a pure function has no side effects visible to any
observer."

Not so with <Grafast />, we're not talking about pure functions here. If your
step is meant to fetch data from a remote source and return it as part of the
result, then that fetch is not a side effect of the step; it's the main event.
If nothing ever depends on that data, then we can determine it wasn't needed and
we can "tree shake" the step out of the plan such that that fetch never happens.
Similarly, if two steps are doing the exact same thing, we may be able to
de-duplicate them such that two fetches become one.

For <Grafast />, a side effect is something that should happen _even if_ nothing
ever depends on the result. It's something that must not be tree-shaken away or
de-duplicated. The most common use case for this is mutations: adding, deleting,
or updating state in your backend storage, triggering an event to be sent, etc.;
however it may also be used for things like logging where you want the log
statement to run even if nothing ever depends on the output of the step:

```ts
const $step = doSomething();

// Nothing depends on this, but because it hasSideEffects it will not be tree
// shaken away, and will be executed.
sideEffect($step, (value) => void console.log(value));

return $step;
```

If you're looking for the equivalent of a "pure function" in Gra*fast*, the
closest is a step that `isSyncAndSafe`...

:::

### isSyncAndSafe

:::danger

This is a very dangerous optimization, only use it if you're 100% sure you know
what you are doing!

:::

Setting this true is a performance optimization, but it comes with strong rules;
we do not test you comply with these rules (as that would undo the performance
gains) but should you break them the behaviour is undefined (and, basically, the
schema may no longer be GraphQL compliant).

Do not set this true unless the following hold:

- The `execute` method must be a regular (not async) function
- The `execute` method must NEVER return a promise, iterator, or similar
- The values within the list returned from `execute` must NEVER include promises,
  iterators, or similar
- The result of calling `execute` should not differ after a
  `step.hasSideEffects` has executed (i.e. it should be pure, only dependent on
  its deps and use no external state)

It's acceptable for the `execute` method to throw if it needs to, but this will
impact every batched value.

This optimisation applies to the majority of the built in plans and allows the
engine to execute without needing to resolve any promises which saves precious
event-loop ticks.

### isOptimized

This is set `true` after the step has been optimized.

### allowMultipleOptimizations

Set this true if your plan's optimize method can be called a second time.

:::danger Your dependencies may change classes!

In this situation it's likely that your dependencies (or their dependencies)
will not be what you expect them to be (e.g. a `PgSelectSingleStep` might
become an `AccessStep` due to having been optimized). This, and the fact
that it's rarely needed, is why it's not enabled by default.

:::

### metaKey

_EXPERIMENTAL_

You may optionally set this to indicate a key to use for which `meta` object to
be passed in to `execute` (typically used for caching). To make it unique to
the instance of your step, in the constructor after calling `super()`, set it
as `this.metaKey = this.id;`. If you want to share the same `meta` object
between all steps of a given class, that class may set `metaKey` to be the name
of the class. You can even set it to a shared value between multiple step
classes (a "family" of step classes) should that make sense. By default no
`metaKey` is set, and your class will therefore have no `meta` object.

:::tip[Inspiration]

The `loadMany` and `loadOne` standard steps make use of this key to optimize
value caching, you may want to look at them for more inspiration.

:::

### optimizeMetaKey

_EXPERIMENTAL_

Makes the `meta` property available on `optimize` options.

:::tip[Inspiration]

The `list` and `object` standard steps make use of this key for caching
constants consistently, you may want to look at them for more inspiration.

:::

[plan resolvers]: ./plan-resolvers
[getting started]: ./getting-started
[standard steps]: ./standard-steps/index.mdx
